package com.protocols.sctp;
import java.rmi.*;

import java.rmi.registry.*;

import java.rmi.server.*;
import java.util.Arrays;
import java.util.Properties;
import java.util.Random;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.*;

import com.Main.MyServiceInterface;
import com.protocols.GeneralProtocolData;
import com.protocols.ProtoOperations;
import com.protocols.sctp.m3ua.M3uaASPAC;
import com.protocols.sctp.m3ua.M3uaHandler;
import com.protocols.sctp.m3ua.M3uaHandlerInterface;


 

public class TcpHandler extends java.rmi.server.UnicastRemoteObject implements SctpHandlerInterface,MyServiceInterface{

	private Socket mySocket ;
	private DataOutputStream outStream ;
	private DataInputStream inStream;
	private Registry Registry;  
    private M3uaHandlerInterface M3uaHandler;
    public Logger Log = Logger.getLogger(this.getClass().getName());
    Boolean logInfoEnabled=false;
    
    
    // Default Configuration
    int layerServiceport=9001;
	String sctphandlerurl= "SctpHandler";
	String sctpPeerIp=	"localhost";
	int sctpPeerPort=2906;		
	String m3uahandlerurl= "M3uaHandler";
	int ConnRetry=3000;		
	String logfile= "var/log/sctp.log";
	Level loglevel= Level.OFF;
	String sctpMode="client";
    
    
    public TcpHandler() throws RemoteException {
		super();		
	}

	public void initialise() throws Exception{	
		
			// Loading the Config File
			Properties properties=null;
			try{			
				properties = new java.util.Properties();
				properties.load( new FileInputStream("General.conf"));
			}catch( IOException e){
				System.out.println("Couldn't locate the Config File");				
				
			}
			
			// Configuring the Logger
			logfile= properties.getProperty("sctp.log.filepath");
			loglevel= Level.parse(properties.getProperty("sctp.log.level"));
						
			FileHandler filehandler=null;
			try{
				filehandler= new FileHandler(logfile,true);
				filehandler.setFormatter(new SimpleFormatter());
				Log.addHandler(filehandler);
				Log.setLevel(loglevel);				
			}catch( IOException e){
				System.out.println("Issue Opening the Log File. Setting the Log Level to 'OFF'"+"Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));
				Log.setLevel(Level.OFF);	
			}
			
			logInfoEnabled=Log.isLoggable(Level.INFO);	
			
			// Layer Service Initialisation
			
			layerServiceport=Integer.valueOf(properties.getProperty("layerservice.port"));
			sctphandlerurl= properties.getProperty("sctp.layerservice.url");
			sctpPeerIp=	properties.getProperty("sctp.peer.ip");
			sctpPeerPort=Integer.valueOf(properties.getProperty("sctp.peer.port"));		
			m3uahandlerurl= properties.getProperty("m3ua.layerservice.url");
			ConnRetry=Integer.valueOf(properties.getProperty("connectionretryinterval"));	
			sctpMode=properties.getProperty("sctp.mode");
			
			// Obtaining the Registry	
			try{
				Registry = LocateRegistry.createRegistry( layerServiceport );
				if(logInfoEnabled)Log.info("Created Registry on Port:"+layerServiceport);
			}catch(RemoteException e){
				Registry = LocateRegistry.getRegistry( layerServiceport );	
				if(logInfoEnabled)Log.info("Obtained Registry from Port:"+layerServiceport);
			}
				
			// Starting the Layer Service	
			Registry.rebind(sctphandlerurl, this);			
			if(logInfoEnabled)Log.info("Bound SCTP Handler on Url:"+sctphandlerurl);
		
		
	}
	


	private void establishSctpClientConnection() throws Exception
	{	
		Boolean retry=true;
		while(retry)
		{
			try{
				mySocket = new Socket(sctpPeerIp, sctpPeerPort);
				retry=false;
			}catch(IOException e){	
				Log.severe("Couldn't Establish the SCTP Connection to "+sctpPeerIp+":"+sctpPeerPort+". Retry in "+ConnRetry+" Secs."+"Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));
				Thread.sleep(ConnRetry);
			}
		}
		if(logInfoEnabled)Log.info("Established the SCTP Connection to "+sctpPeerIp+":"+sctpPeerPort);
		
		outStream = new DataOutputStream(mySocket.getOutputStream());
		inStream = new DataInputStream(mySocket.getInputStream());	
	}
	
	private void establishM3uaConnection()throws Exception
	{	
		Boolean retry=true;
		while(retry)
		{
			try{
				M3uaHandler=(M3uaHandlerInterface)(Registry.lookup(m3uahandlerurl));
				retry=false;
			}catch(Exception e){	
				Log.severe("Couldn't Obtained the M3UA Instance. Retry in "+ConnRetry+" Secs."+"Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));
				Thread.sleep(ConnRetry);
			}
		}				
		if(logInfoEnabled)Log.info("Obtained M3uaHandler Instance");

		
		
	}


    public void StartClientOperation() throws Exception{
    	try{
	    	byte []input =new byte[300];
	    	int count=0;
	    	while ( true){
				  int length=inStream.read(input);				  
				  if(length>0){		
					  if(logInfoEnabled)Log.info("Received Byte Stream, Creating the Thread:"+ProtoOperations.getHexString(input));
					  new TreatInCommingTcp(ProtoOperations.extractByteArray(input, 0, length), M3uaHandler, outStream,  new Random().nextInt(1000000000)).start();
				  }	
				  else if(length==-1){		
					  // Socket Disconnected from Server
					  establishSctpClientConnection();
				  }	
				  
				  
				count++;  
			}
    	}catch(IOException e){
    		establishSctpClientConnection();
    		StartClientOperation();
	    }
    }
    	
	  
    public void StartServerOperation() throws Exception{
    	try{	    	    	
	    	while(true){				
				ServerSocket welcomeSocket = new ServerSocket(sctpPeerPort);
				mySocket = welcomeSocket.accept();      
			     
				outStream = new DataOutputStream(mySocket.getOutputStream());
				inStream = new DataInputStream(mySocket.getInputStream());
				if(logInfoEnabled)Log.info("Client Connection Accepted:"+mySocket.getRemoteSocketAddress().toString());
				  
				byte []input =new byte[300];
				
				while ( true){
				  int length=inStream.read(input);
				  
				  if(length>0){
					  if(logInfoEnabled)Log.info("Received Byte Stream, Creating the Thread:"+ProtoOperations.getHexString(input));
					  new TreatInCommingTcp(ProtoOperations.extractByteArray(input, 0, length), M3uaHandler, outStream, new Random().nextInt(1000000000)).start();
				  }						  
				 }
				
			}
	    	
	    	
	    	
    	}catch(IOException e){
    		
	    }
    }
    

	
    

	@Override
	public void m3uaToSctp(long sessionid,GeneralProtocolData m3uamsg)throws RemoteException{
		try{
			byte [] tosend=((GeneralProtocolData)m3uamsg).getByteArray();
			if(logInfoEnabled)Log.info(sessionid+": Sending Byte Stream, Creating the Thread:"+ProtoOperations.getHexString(tosend));
			outStream.write(tosend);
		}catch(Exception e){
			Log.severe("Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));
		}
		
	}


	@Override
	public void startLayer() throws Exception {
		
		try{
			initialise();
			if (sctpMode.equals("client")){
				establishSctpClientConnection();
				establishM3uaConnection();
				StartClientOperation();	
			}
			else{
				establishM3uaConnection();
				StartServerOperation();
			}
			
			
			// Self Check			
			
			
			
		}catch(Exception e){
			Log.severe("Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));
			
		}
		
	}
   
	

   
}